﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DragLocator.cs" company="Jarno Burger">
//   See copyright.txt in the root of this project.
// </copyright>
// <summary>
//   Defines the DragLocator type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace GUI4UFramework.Management
{
    using System.Diagnostics;

    using GUI4UFramework.Graphics;
    using GUI4UFramework.Structural;

    /// <summary>
    /// Remembers when a item gets dragged and moved. And how it moved.
    /// </summary>
    public class DragLocator
    {
        /// <summary>
        /// The control to follow for movements.
        /// </summary>
        private readonly Control controlToFollow;

        /// <summary>
        /// The manager that can read inputs.
        /// </summary>
        private readonly InputManager manager;

        /// <summary>
        /// Initializes a new instance of the <see cref="DragLocator"/> class.
        /// </summary>
        /// <param name="manager">The manager.</param>
        /// <param name="controlToFollow">The control to follow.</param>
        public DragLocator(InputManager manager, Control controlToFollow)
        {
            this.controlToFollow = controlToFollow;
            this.manager = manager;
        }

        /// <summary>
        /// Gets a value indicating whether [mouse pressed].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [mouse pressed]; otherwise, <c>false</c>.
        /// </value>
        public bool StateMousePressed { get; private set; }

        /// <summary>
        /// Gets a value indicating whether we are [Dragging].
        /// </summary>
        /// <value>
        ///   <c>true</c> if dragging; otherwise, <c>false</c>.
        /// </value>
        public bool Dragging { get; private set; }

        /// <summary>
        /// Gets the location where the mouse started the drag. Relative to the parent.
        /// </summary>
        /// <value>
        /// The started drag location.
        /// </value>
        public DVector2 DragStartLocation { get; private set; }

        /// <summary>
        /// Gets the location where we are currently dragging with the mouse. Relative to the parent.
        /// </summary>
        /// <value>
        /// The current drag location.
        /// </value>
        public DVector2 DragCurrentLocation { get; private set; }

        /// <summary>
        /// Gets the location where we ended dragging with the mouse. Relative to the parent.
        /// </summary>
        /// <value>
        /// The drag end location.
        /// </value>
        public DVector2 DragEndLocation { get; private set; }

        /// <summary>
        /// Gets the amount of movement after starting of the drag.
        /// </summary>
        /// <value>
        /// The drag current delta.
        /// </value>
        public DVector2 DragCurrentDelta
        {
            get
            {
                return this.DragCurrentLocation - this.DragStartLocation;
            }
        }

        /// <summary>
        /// Gets the current mouse location.
        /// </summary>
        /// <value>
        /// The mouse location.
        /// </value>
        public DVector2 MouseLocation { get; private set; }
        
        /// <summary>
        /// Reads if we are dragging, and react to it if we are.
        /// </summary>
        public void Read()
        {
            // AQCUIRE VALUES : take mouse input
            this.MouseLocation = this.manager.ReadMouseLocation();
            this.StateMousePressed = this.manager.ReadLeftMousePressed();

            // VALIDATE : if we have nothing to follow then return
            if (this.controlToFollow == null)
            {
                return;
            }

            // VALIDATE : if the control is not visible then return
            if (this.controlToFollow.Config.Visible == false)
            {
                return;
            }

            // DO LOGIC : if mouse was pressed on top of the control and we were not dragging yet.. then we have started dragging
            if (this.ControlMousedOver() && this.StateMousePressed && this.Dragging == false)
            {
                this.DraggingStart();
            }

            // DO LOGIC : if mouse was released and we are still dragging, then we stop dragging
            if (!this.StateMousePressed && this.Dragging)
            {
                this.DraggingEnd();
            }

            // DO LOGIC : if we are dragging , the update current location
            if (this.Dragging)
            {
                this.DraggingUpdate();
            }
        }

        /// <summary>
        /// Gets the relative mouse location to the parent OR the window.
        /// </summary>
        /// <returns>The relative location.</returns>
        private DVector2 GetMousePosition()
        {
            return this.manager.ReadMouseLocation();
        }

        /// <summary>
        /// Checks if the moused is over this control.
        /// </summary>
        /// <returns>True when mouse is over.</returns>
        private bool ControlMousedOver()
        {
            return this.controlToFollow.State.MouseHoveringOver;
        }

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format(
                                "Mouse:{0},{1} Dragging:{2} Start:{3} Current:{4} End:{5} Delta:{6}",
                                this.MouseLocation,
                                this.StateMousePressed,
                                this.Dragging,
                                this.DragStartLocation,
                                this.DragCurrentLocation,
                                this.DragEndLocation,
                                this.DragCurrentDelta);
        }

        /// <summary>
        /// Starts the dragging.
        /// </summary>
        private void DraggingStart()
        {
            this.DragStartLocation = this.GetMousePosition();

            Debug.WriteLine(
                            "Start dragging {0}, at {1}",
                            this.controlToFollow.Name, 
                            this.DragStartLocation);

            this.Dragging = true;
        }

        /// <summary>
        /// Updates the dragging.
        /// </summary>
        private void DraggingUpdate()
        {
            this.DragCurrentLocation = this.GetMousePosition();

            Debug.WriteLine(
                "Currently dragging {0}, at {1}",
                this.controlToFollow.Name,
                this.DragCurrentLocation);
        }

        /// <summary>
        /// End the dragging.
        /// </summary>
        private void DraggingEnd()
        {
            this.DragEndLocation = this.GetMousePosition();

            Debug.WriteLine(
                "Ended dragging {0}, at {1}",
                this.controlToFollow.Name,
                this.DragEndLocation);

            this.Dragging = false;

        }
    }
}
